package com.gupaoedu.vip.pattern.springmvc1.servlet;

import com.gupaoedu.vip.pattern.springmvc1.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Pattern;

public class MyDispatherServlet extends HttpServlet {

    //保存配置文件的信息
    private Properties contextConfig = new Properties();
    //保存classname
    private List<String> classNames = new ArrayList<String>();
    //IOC容器
    private Map<String,Object> ioc = new HashMap<String, Object>();

//    private Map<String, Method> handlerMapping = new HashMap<String, Method>();

    private List<HandlerMapping> handlerMapping = new ArrayList<HandlerMapping>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatcher(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        HandlerMapping handlerMapping = getHandler(req);
        if(handlerMapping == null){
            resp.getWriter().write("404 not found");
        }
        Class<?> [] paramTypes = handlerMapping.getMethod().getParameterTypes();
        Object[] paramValues = new Object[paramTypes.length];
        Map<String,String[]> params = req.getParameterMap();
        for (Map.Entry<String, String[]> param : params.entrySet()) {
            String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll("\\s","");
            if(!handlerMapping.getParamIndexMapping().containsKey(param.getKey())){continue;}
            int index = handlerMapping.paramIndexMapping.get(param.getKey());
            paramValues[index] = convert(paramTypes[index],value);
        }
        if(handlerMapping.paramIndexMapping.containsKey(HttpServletRequest.class.getName())) {
            int reqIndex = handlerMapping.paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[reqIndex] = req;
        }

        if(handlerMapping.paramIndexMapping.containsKey(HttpServletResponse.class.getName())) {
            int respIndex = handlerMapping.paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[respIndex] = resp;
        }
        Object returnValue = handlerMapping.method.invoke(handlerMapping.controller, paramValues);
        if(returnValue == null ||returnValue instanceof  Void ){return;}
        resp.getWriter().write(returnValue.toString());
    }

    private Object convert(Class<?> type,String value){
        //如果是int
        if(Integer.class == type){
            return Integer.valueOf(value);
        }
        else if(Double.class == type){
            return Double.valueOf(value);
        }
        return value;
    }

    private HandlerMapping getHandler(HttpServletRequest req) {
        if(handlerMapping.isEmpty()){return null;}
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");
        for (HandlerMapping mapping : handlerMapping) {
            if(!mapping.getPattern().matcher(url).matches()){continue;}
            return mapping;
        }
        return null;
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        // TODO 加载配置文件
        doLoadConfig(servletConfig.getInitParameter("contextConfigLocation"));
        // TODO 扫描相关的类
        doScanner(contextConfig.getProperty("scanPackage"));
        // TODO 初始化相关的类 放入到相关的IOC容器之中
        doInstance();
        // TODO 完成依赖注入 DI操作
        doAutowired();
        // TODO 初始化HandlerMapping
        initHandlerMapping();
        System.out.println("init complete");
    }

    //把带有@controller类中的方法和对应url映射
    private void initHandlerMapping() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if(!clazz.isAnnotationPresent(MyController.class)){return;}
            String baseUrl = "";
            if(clazz.isAnnotationPresent(MyRequestMapping.class)){
                MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(MyRequestMapping.class)){return;}
                MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
                String regex = ("/"+baseUrl+"/"+requestMapping.value()).replaceAll("/+","/");
                Pattern pattern = Pattern.compile(regex);
                this.handlerMapping.add(new HandlerMapping(entry.getValue(),method,pattern));
                System.out.println("mapped:"+method+pattern);
            }

        }

    }

    //自动装配  给定义的字段赋值
    private void doAutowired() {
        if(ioc.isEmpty()){return;}
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //取出所有定义的字段
            Field [] fields = entry.getValue().getClass().getDeclaredFields();
            //遍历字段，取出加了autowired的字段。完成自动装配
            for (Field field : fields) {
                if(!field.isAnnotationPresent(MyAutowired.class)){return;}
                MyAutowired autowired = field.getAnnotation(MyAutowired.class);
                String beanName = autowired.value().trim();
                //如果字段为空，则把类型作为key
                if("".equals(beanName)){
                    beanName = field.getType().getName();
                }
                field.setAccessible(true);
                try {
                    //用反射给需要自动装配的字段赋值
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        }

    }

    //将需要实例化的类放在IOC容器中
    private void doInstance() {
        if(classNames.isEmpty()){return;}
        try {
            for (String classname : classNames) {
                Class<?> clazz = Class.forName(classname);
                if(clazz.isAnnotationPresent(MyController.class)){
                    Object instance = clazz.newInstance();
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName, instance);
                }else if(clazz.isAnnotationPresent(MyService.class)){
                    String beanName = clazz.getSimpleName();
                    MyService service = clazz.getAnnotation(MyService.class);
                    if("".equals(beanName)){
                        beanName = toLowerFirstCase(beanName);
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                    //遍历service实现的接口类,把接口的包加类名作为key，实例本身放在IOC容器中
                    for (Class<?> i: clazz.getInterfaces()) {
                        if(ioc.containsKey(i.getName())){
                            throw new Exception("the "+i.getName()+" is exists");
                        }
                        ioc.put(i.getName(), instance);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private String toLowerFirstCase(String simpleName) {
        char [] chars = simpleName.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }

    //扫描所有的类，为IOC容器初始化做准备
    private void doScanner(String scanPackage) {
        URL url = this.getClass().getClassLoader().getResource("/"+scanPackage.replaceAll("\\.","/"));
        File classpath = new File(url.getFile());
        for (File file:classpath.listFiles() ) {
            if(file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else{
                if(!file.getName().endsWith(".class")){continue;}
                String className = (scanPackage+"."+file.getName().replace(".class",""));
                classNames.add(className);
            }
        }
    }

    //加载配置文件
    private void doLoadConfig(String contextConfigLocation) {
        InputStream fis = null;
        fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            contextConfig.load(fis);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public class HandlerMapping {

        private Object controller;

        private Method method;

        private Pattern pattern;

        private Map<String,Integer> paramIndexMapping;

        public HandlerMapping(Object controller, Method method, Pattern pattern) {
            this.controller = controller;
            this.method = method;
            this.pattern = pattern;
            this.paramIndexMapping = new HashMap<String, Integer>();
            putParamIndexMapping(method);
        }

        public Object getController() {
            return controller;
        }

        public Method getMethod() {
            return method;
        }

        public Pattern getPattern() {
            return pattern;
        }

        public Map<String, Integer> getParamIndexMapping() {
            return paramIndexMapping;
        }

        private void putParamIndexMapping(Method method){
            Annotation [] [] pa = method.getParameterAnnotations();
            for (int i = 0; i < pa.length; i++) {
                for (Annotation a:pa[i]) {
                    if(a instanceof MyRequestParam){
                        String paramName = ((MyRequestParam) a).value();
                        if(!"".equals(paramName)){
                            paramIndexMapping.put(paramName, i);
                        }
                    }
                }
            }

            Class<?> [] paramTypes = method.getParameterTypes();
            for (int i = 0; i < paramTypes.length; i++) {
                Class<?> type = paramTypes[i];
                if(type==HttpServletRequest.class||type==HttpServletResponse.class){
                    paramIndexMapping.put(type.getName(), i);
                }
            }
        }
    }
}
